package crp.extensions.scy.storage;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import com.qwlabs.lang.S2;
import com.qwlabs.storage.s3.S3StorageEngine;
import crp.core.resource.domain.models.AttachmentType;
import crp.core.resource.domain.models.CreateCourseResourceCommand;
import crp.core.resource.domain.models.ResourceSourceType;
import com.qwlabs.storage.models.StorageObject;
import crp.core.timetable.domain.models.TimetableEntity;
import crp.extensions.scy.SCY;
import crp.extensions.scy.models.SCYResourceSpaceEntity;
import crp.extensions.scy.repositories.SCYResourceSpaceRepository;
import crp.extensions.scy.storage.SCYStorageEngine;
import lombok.AllArgsConstructor;
import lombok.Builder;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Builder
@AllArgsConstructor
public class SCYResourceStorage {
    private static final DateTimeFormatter NAME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
    private static final int STEP_MINUTES = 30;
    private static final Duration RESOURCE_INTERVAL = Duration.ofHours(1);
    private final Supplier<List<StorageObject>> storageObjects = Suppliers.memoize(this::loadStorageObjects);

    private final TimetableEntity timetable;
    private final SCYResourceSpaceRepository resourceSpaceRepository;
    private final SCYStorageEngine storageEngine;

    public StorageObject get(CameraType cameraType) {
        var resourceSpaceCode = getResourceSpaceCode();
        if (S2.isBlank(resourceSpaceCode)) {
            return null;
        }
        return StorageObject.of(resourceSpaceCode,
                getObjectName(cameraType.getRaw(), resourceSpaceCode),
                SCY.ENV,
                cameraType.name
        );
    }

    private String getResourceSpaceCode() {
        var spaceName = String.format("%s-%s",
                timetable.getSpace().getLocation().getName(),
                timetable.getSpace().getName());
        return resourceSpaceRepository.findByName(spaceName)
                .map(SCYResourceSpaceEntity::getSpaceCode)
                .orElse("");
    }


    private String getObjectName(String cameraType, String spaceCode) {
        LocalDateTime startAt = correctionStartAt(timetable.getStartAt());
        LocalDateTime endAt = startAt.plus(RESOURCE_INTERVAL);
        return String.format("%s-%s-%s-%s.mp4",
                cameraType,
                spaceCode,
                startAt.format(FORMATTER),
                endAt.format(FORMATTER)
        );
    }

    private LocalDateTime correctionStartAt(Instant instant) {
        var time = LocalDateTime.ofInstant(instant, ZoneOffset.ofHours(8));
        return time.plusMinutes(correctionMinutes(time));
    }

    private int correctionMinutes(LocalDateTime time) {
        if (time.getMinute() == STEP_MINUTES) {
            return 0;
        }
        if (time.getMinute() > STEP_MINUTES) {
            return STEP_MINUTES - time.getMinute();
        }
        return -time.getMinute();
    }

    private List<StorageObject> loadStorageObjects() {
        List<StorageObject> storageObjects = Lists.newArrayList();
        find(CameraType.FACULTY)
                .ifPresent(storageObjects::add);
        find(CameraType.STUDENT)
                .ifPresent(storageObjects::add);
        find(CameraType.VGA)
                .ifPresent(storageObjects::add);
        return storageObjects;
    }

    private Optional<StorageObject> find(CameraType cameraType) {
        return Optional.ofNullable(get(cameraType))
                .filter(storageObject -> storageEngine.exist(storageObject.getBucket(), storageObject.getObjectName()));
    }

    public boolean isEmpty() {
        return getStorageObjects().isEmpty();
    }

    private List<StorageObject> getStorageObjects() {
        return Optional.ofNullable(storageObjects.get()).orElse(List.of());
    }

    public CreateCourseResourceCommand createCommand() {
        return CreateCourseResourceCommand.builder()
                .course(timetable.getCourse())
                .name(buildName())
                .type(AttachmentType.VIDEO)
                .startAt(timetable.getStartAt())
                .endAt(timetable.getEndAt())
                .sourceType(ResourceSourceType.SYNC)
                .space(timetable.getSpace())
                .items(getStorageObjects())
                .build();
    }

    private String buildName() {
        var time = LocalDateTime.ofInstant(timetable.getStartAt(), ZoneOffset.ofHours(8));
        if (Objects.isNull(timetable.getStartSlot())) {
            return String.format("%s-%s-录播",
                    timetable.getCourse().getName(),
                    time.format(NAME_FORMATTER));
        }
        return String.format("%s-%s-第%d节-录播",
                timetable.getCourse().getName(),
                time.format(NAME_FORMATTER),
                timetable.getStartSlot());
    }

    private enum CameraType {
        FACULTY("0", "教师机"),
        STUDENT("1", "学生机"),
        VGA("2", "vga电脑画面");
        private final String raw;
        private final String name;

        CameraType(String raw, String name) {
            this.raw = raw;
            this.name = name;
        }

        public String getRaw() {
            return raw;
        }

        public String getName() {
            return name;
        }
    }
}
